package ln.gather.jsr.jndi.repository;

import ln.gather.jsr.jndi.function.ThrowableAction;
import ln.gather.jsr.jndi.function.ThrowableFunction;

import javax.naming.*;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

/**
 * <pre>
 * 基于 JNDI 实现的组件仓库
 *
 * 仓库初始化：
 * 1 初始化类加载器
 * 1 初始化环境上下文
 *
 * 仓库销毁：
 * 1. 清除仓库组件缓存 - 父类实现
 * 2. 关闭 JNDI 环境上下文
 *
 * </pre>
 */
public class JndiComponentRepository extends AbstractComponentRepository {

    private final String COMPONENT_ENV_CONTEXT_NAME = "java:comp/env";

    /**
     * JNDI环境上下文
     */
    private Context envContext;

    private ClassLoader classLoader;

    /**
     * 1 初始化
     */
    @Override
    public void initialize() {
        // 1.1 初始化类加载器
        initClassLoader();
        // 1.2 初始化环境上下文
        initEnvContext();
    }

    /**
     * 1.1 初始化类加载器
     */
    private void initClassLoader() {
        if (classLoader == null) {
            classLoader = Thread.currentThread().getContextClassLoader();
        }
    }

    /**
     * 1.2 初始化环境上下文
     */
    private void initEnvContext() {
        if (envContext != null) {
            return;
        }
        Context context = null;
        try {
            context = new InitialContext();
            this.envContext = (Context) context.lookup(COMPONENT_ENV_CONTEXT_NAME);
        } catch (NamingException e) {
            throw new RuntimeException(e);
        } finally {
            close(context);
        }

    }

    /**
     * 关闭资源
     */
    private void close(Context context) {
        if (context != null) {
            ThrowableAction.execute(context::close);
        }
    }

    /**
     * 销毁
     */
    @Override
    protected void doDestroy() {
        // 关闭环境上下文
        close(this.envContext);
    }

    /**
     * 加载组件，需要子类实现
     *
     * @see Context
     * @see
     */
    @Override
    public <C> C loadComponent(String name) {
        return (C) executeInContextIgnoreException(envContext, context -> context.lookup(name));
    }

    /**
     * 遍历所有组件的名称，需要子类实现
     */
    @Override
    public Set<String> listComponentNames() {
        return listComponentNames("/");
    }

    /**
     * 按照名称来遍历所有的组件名称
     */
    public Set<String> listComponentNames(String name) {
        return executeInContextIgnoreException(envContext, context -> {
            // 获取 JNDI 目录中的资源
            NamingEnumeration<NameClassPair> e = executeInContextIgnoreException(context, ctx -> ctx.list(name));
            if (e == null) {
                return Collections.emptySet();
            }
            Set<String> allComponentNames = new LinkedHashSet<>();
            while (e.hasMoreElements()) {
                NameClassPair element = e.nextElement();
                String className = element.getClassName();
                Class<?> targetClass = classLoader.loadClass(className);
                if (Context.class.isAssignableFrom(targetClass)) {
                    // 如果当前名称是目录（Context 的实现类）的话，就递归继续查找
                    allComponentNames.addAll(listComponentNames(element.getName()));
                } else {
                    // 否则，当前名称是绑定目标实现类的话，添加该名称到集合中
                    String fullName = name.startsWith("/") ? element.getName() : name + "/" + element.getName();
                    allComponentNames.add(fullName);
                }
            }
            return allComponentNames;
        });
    }

    /**
     * 注册组件实例到实例仓库中
     *
     * @param name      组件名称
     * @param component 组件实例
     */
    @Override
    public void registerComponent(String name, Object component) {
        executeInContextIgnoreException(this.envContext, context -> {
            context.bind(name, component);
            return null;
        });
    }
}
